/*
-----------------------------------------------------------------------------
This source file is part of Labor3D
(Labor 3D Graphics Engine)

Copyright (c) 2009-2020 Rock Mao
* creator : Rock Mao
* created : 2009-5-2   22:29
* filename: LaborFrustum.h
-----------------------------------------------------------------------------
*/
#ifndef __LABORFRUSTUM_H__
#define __LABORFRUSTUM_H__

#include "../LaborCoreDef.h"
#include "LaborRenderSystemEnum.h"
#include "../Math/LaborMathInclude.h"

NAMESPACE_LABOR3D_BEGIN

class LABORCORE_API CLFrustum
{
protected:
	/// Orthographic or perspective?
	eLProjectionType mProjType;

	/// y-direction field-of-view (default 45)
	float m_fFOVy;
	/// Far clip distance - default 10000
	float m_fZFar;
	/// Near clip distance - default 100
	float m_fZNear;
	/// x/y viewport ratio - default 1.3333
	float m_fAspect;

	/// The 6 main clipping planes
	CLPlane m_frustumPlanes[6];

	/// Pre-calced projection matrix
	CLMatrix4x4 m_matrixProjection;
	/// Pre-calced view matrix
	CLMatrix4x4	m_matrixView;
	/// Pre-calced inv view matrix
	CLMatrix4x4	m_matrixInvView;
	/// Pre-calced inv project matrix
	CLMatrix4x4	m_matrixInvProjection;
	/// Pre-calced view*project matrix
	CLMatrix4x4	m_matrixViewProjection;
	/// Pre-calced view*project invert matrix
	CLMatrix4x4	m_matrixInvViewProjection;


	bool m_bFrustumChanged;
	/// Something re the view pos has changed
	bool m_bViewChanged;

	/** Temp coefficient values calculated from a frustum change,
	used when establishing the frustum planes when the view changes
	*/
	float m_fCoeffL[2], m_fCoeffR[2], m_fCoeffB[2], m_fCoeffT[2];

	CLPoint3 m_pointWorldSpaceCorners[8];




	/// Is this frustum to act as a reflection of itself?
	bool m_bReflectEnabled;
	CLMatrix4x4 m_matrixReflect;
	CLPlane m_planReflect;
public:
	CLFrustum();
	~CLFrustum();
public:
	bool isVisiable( const CLPoint3 &bound, eLFrustumPlane *cullBy = NULL ) ;
	bool isVisiable( const CLSphere &bound, eLFrustumPlane *cullBy = NULL ) ;
	bool isVisiable( const CLAABB &bound, eLFrustumPlane *cullBy = NULL ) ;

	void enableReflection( CLPlane &plane );
	void disableReflection();

	/// @name Sets functions
	/// Sets frustum attribute
	/// @{
	void setFOVy( float fFOVy );
	void setAspect( float fAspect );
	void setFarPlaneDistance( float fFarDist );
	void setNearPlaneDistance( float fNearDist );
	/// @}

	/// @name Gets functions
	/// Gets frustum attribute
	/// @{
	float getFOVy() const;
	float getAspect() const;
	float getFarPlaneDistance() const;
	float getNearPlaneDistance() const;

	virtual const CLMatrix4x4 &getViewMatrix();
	virtual const CLMatrix4x4 &getProjectionMatrix();
	virtual const CLMatrix4x4 &getViewProjectionMatrix();
	virtual const CLMatrix4x4 &getViewProjectionMatrixInverse();
	virtual const CLMatrix4x4 &getViewMatrixInverse();
	virtual const CLMatrix4x4 &getProjectionMatrixInverse();
	/// @}

	const CLPoint3 &getFarLeftUpPos();
	const CLPoint3 &getFarLeftDownPos();
	const CLPoint3 &getFarRightUpPos();
	const CLPoint3 &getFarRightDownPos();

	const CLPoint3 &getNearLeftUpPos();
	const CLPoint3 &getNearLeftDownPos();
	const CLPoint3 &getNearRightUpPos();
	const CLPoint3 &getNearRightDownPos();
protected:

	// Internal functions for calcs
	virtual void _updateFrustum(void);
	virtual void _updateView(void);

	virtual void _viewChanged();
	virtual void _frustumChanged();
};


//////////////////////////////////////////////////////////////////////////
inline void CLFrustum::_viewChanged(){
	m_bViewChanged = true;
}
inline void CLFrustum::_frustumChanged(){
	m_bFrustumChanged = true;
}

//////////////////////////////////////////////////////////////////////////
inline void CLFrustum::setFOVy( float fFOVy ){
	m_fFOVy = fFOVy;
	_viewChanged();
	_frustumChanged();
}
inline void CLFrustum::setAspect( float fAspect ){
	m_fAspect = fAspect;
	_viewChanged();
	_frustumChanged();
}
inline void CLFrustum::setFarPlaneDistance( float fFarDist ){
	m_fZFar = fFarDist;
	_viewChanged();	
	_frustumChanged();
}
inline void CLFrustum::setNearPlaneDistance( float fNearDist ){
	m_fZNear = fNearDist;
	_viewChanged();
	_frustumChanged();
}

//////////////////////////////////////////////////////////////////////////
inline float CLFrustum::getFOVy() const{
	return m_fFOVy;
}
inline float CLFrustum::getAspect() const{
	return m_fAspect;
}
inline float CLFrustum::getFarPlaneDistance() const{
	return m_fZFar;
}
inline float CLFrustum::getNearPlaneDistance() const{
	return m_fZNear;
}

inline const CLPoint3 &CLFrustum::getFarLeftUpPos(){
	_updateView();
	// -x, y
	return m_pointWorldSpaceCorners[7];
	//	return m_pointWorldSpaceCorners[4];
}
inline const CLPoint3 &CLFrustum::getFarLeftDownPos(){
	_updateView();
	return m_pointWorldSpaceCorners[4];
	//	return m_pointWorldSpaceCorners[7];
}
inline const CLPoint3 &CLFrustum::getFarRightUpPos(){
	_updateView();
	return m_pointWorldSpaceCorners[6];
	//	return m_pointWorldSpaceCorners[5];
}
inline const CLPoint3 &CLFrustum::getFarRightDownPos(){
	_updateView();
	return m_pointWorldSpaceCorners[5];
	//	return m_pointWorldSpaceCorners[6];
}


inline const CLPoint3 &CLFrustum::getNearLeftUpPos(){
	_updateView();
	// -x, y
	return m_pointWorldSpaceCorners[1];
	//	return m_pointWorldSpaceCorners[4];
}
inline const CLPoint3 &CLFrustum::getNearLeftDownPos(){
	_updateView();
	return m_pointWorldSpaceCorners[2];
	//	return m_pointWorldSpaceCorners[7];
}
inline const CLPoint3 &CLFrustum::getNearRightUpPos(){
	_updateView();
	return m_pointWorldSpaceCorners[0];
	//	return m_pointWorldSpaceCorners[5];
}
inline const CLPoint3 &CLFrustum::getNearRightDownPos(){
	_updateView();
	return m_pointWorldSpaceCorners[3];
	//	return m_pointWorldSpaceCorners[6];
}


NAMESPACE_LABOR3D_END
#endif